library ArmorUtils requires Logarithm, optional IntuitiveDamageSystem
    //******************************************************************************
    //* BY: Rising_Dusk
    //* 
    //* This is used to get the exact armor of a given unit. It deals damage to the
    //* unit in order to determine armor, meaning that damage detection systems will
    //* detect it. If your map has the Intuitive Damage Detection System (IDDS) in
    //* it, it will use that system's internal ignored damage type for the check.
    //* Using that removes the need to disable/enable any damage detection triggers
    //* to avoid infinite loops.
    //* 
    //* The attacktype constant, ATTACK_TYPE_USED, is by default set to use
    //* ATTACK_TYPE_CHAOS. Chaos is used because it deals 100% to all types, so if
    //* you change it in your map, make sure you update the constant to some type
    //* with 100% damage to all armor types.
    //* 
    //* This system can also be used as a means to detect if a unit is invulnerable
    //* or not. If GetUnitArmor returns ARMOR_INVULNERABLE, then the unit is
    //* invulnerable. The value for it is entirely random and will never be the
    //* actual armor value for a unit, so there should be no conflicts.
    //* 
    //* Damage reduction in WC3 due to negative armor is capped at -71%, which has
    //* an equivalent armor value of -20. If you have a unit with less than -20
    //* armor, this system will always return exactly -20.
    //* 
    //* Default WC3 gameplay constants have the 'Armor Damage Reduction Multiplier'
    //* set to 0.06. If you change this constant for your map, be sure to adjust the
    //* ARMOR_REDUCTION_MULTIPLIER constant in this script to match.
    //* 
    //* Example Usage:
    //*     local real armor = GetUnitArmor(MyUnit)
    //* 
    //* Two other functions are included in this library. They are used for
    //* calculating a unit's base damage knowing armor-factoring damage and a unit's
    //* armor-factoring damage knowing its base damage. These functions do not
    //* consider armor type in their calculations. If you want armor type damage
    //* adjusting, it is recommended to trigger it.
    //* 
    //* Example Usage:
    //*     local real basedmg = GetFullDamage(MyUnit, Armor)
    //*     local real armodmg = GetReducedDamage(MyUnit, Armor)
    //* 
    //* An objectmerger call is included in this script to automatically generate
    //* the bonus life ability if necessary. If you do not modify the 'AIlz' ability
    //* in your map, you can replace the LIFE_BONUS_SPELL_ID constant with it and
    //* not use the objectmerger call. The 
    //*
    globals
        //Values that should be changed for your map
        private constant real       ARMOR_REDUCTION_MULTIPLIER = 0.06
        private constant integer    LIFE_BONUS_SPELL_ID        = 'lifb'
        private constant attacktype ATTACK_TYPE_USED           = ATTACK_TYPE_CHAOS
        
        //Values that do not need to be changed
                constant real    ARMOR_INVULNERABLE            = 917451.519
        private constant real    DAMAGE_TEST                   = 16.
        private constant real    DAMAGE_LIFE                   = 30.
        private constant real    NATLOG_094                    =-0.061875
    endglobals
    
    ////! external ObjectMerger w3a AIlz lif& anam "GetUnitArmorLifeBonus" ansf "" Ilif 1 30 aite 0
    
    function GetUnitArmor takes unit u returns real
        local real    life = GetWidgetLife(u)
        local real    test = life
        local real    redc = 0.
        local boolean enab = false
        local trigger trig = GetTriggeringTrigger()
        if u != null and life >= 0.405 then
            if GetUnitState(u, UNIT_STATE_MAX_LIFE) <= DAMAGE_TEST then
                //Add max life to keep it alive
                call UnitAddAbility(u, LIFE_BONUS_SPELL_ID)
            endif
            if life <= DAMAGE_LIFE then
                //If under the threshold, heal it for the moment
                call SetWidgetLife(u, DAMAGE_LIFE)
                set test = DAMAGE_LIFE 
            endif
            static if LIBRARY_IntuitiveDamageSystem then
                //Use the IGNORED type in the IDDS if present so it is 100% ignored
                call UnitDamageTargetEx(u, u, DAMAGE_TEST, ATTACK_TYPE_USED, DAMAGE_TYPE_IGNORED, true)
            else
                if trig != null and IsTriggerEnabled(trig) then
                    //Disable the trigger to prevent it registering with damage detection systems
                    call DisableTrigger(trig)
                    set enab = true
                endif
                call UnitDamageTarget(u, u, DAMAGE_TEST, true, false, ATTACK_TYPE_USED, DAMAGE_TYPE_NORMAL, null)
                if enab then
                    //Re-enable the trigger
                    call EnableTrigger(trig)
                endif
            endif
            set redc = (DAMAGE_TEST-test+GetWidgetLife(u))/DAMAGE_TEST
            //Remove the max life ability
            call UnitRemoveAbility(u, LIFE_BONUS_SPELL_ID)
            call SetWidgetLife(u, life)
            set trig = null
            if redc >= 1. then
                //Invulnerable
                return ARMOR_INVULNERABLE
            elseif redc < 0. then
                //Negative Armor
                return -Log(redc+1.)/NATLOG_094
            else
                //Positive Armor
                return redc/(ARMOR_REDUCTION_MULTIPLIER*(1.-redc))
            endif
        endif
        set trig = null
        return 0.
    endfunction
    
    function GetReducedDamage takes real baseDamage, real armor returns real
        if armor >= 0. then
            return baseDamage*(1.-((armor*ARMOR_REDUCTION_MULTIPLIER)/(1.+ARMOR_REDUCTION_MULTIPLIER*armor)))
        else
            return baseDamage*(2.-Pow(0.94,-armor))
        endif
    endfunction
    
    function GetFullDamage takes real damage, real armor returns real
        if armor >= 0. then
            return damage/(1.-((armor*ARMOR_REDUCTION_MULTIPLIER)/(1.+ARMOR_REDUCTION_MULTIPLIER*armor)))
        else
            return damage/(2.-Pow(0.94,-armor))
        endif
    endfunction
    endlibrary